package com.yangxu.hospital.system.modules.security.rest;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wf.captcha.base.Captcha;
import com.yangxu.hospital.common.annotation.rest.AnonymousDeleteMapping;
import com.yangxu.hospital.common.annotation.rest.AnonymousGetMapping;
import com.yangxu.hospital.common.annotation.rest.AnonymousPostMapping;
import com.yangxu.hospital.common.config.RsaProperties;
import com.yangxu.hospital.common.exception.BadRequestException;
import com.yangxu.hospital.common.utils.RedisUtils;
import com.yangxu.hospital.common.utils.RsaUtils;
import com.yangxu.hospital.common.utils.SecurityUtils;
import com.yangxu.hospital.common.utils.StringUtils;
import com.yangxu.hospital.logging.annotation.Log;
import com.yangxu.hospital.system.modules.security.config.bean.LoginCodeEnum;
import com.yangxu.hospital.system.modules.security.config.bean.LoginProperties;
import com.yangxu.hospital.system.modules.security.config.bean.SecurityProperties;
import com.yangxu.hospital.system.modules.security.security.TokenProvider;
import com.yangxu.hospital.system.modules.security.service.OnlineUserService;
import com.yangxu.hospital.system.modules.security.service.dto.AuthUserDto;
import com.yangxu.hospital.system.modules.security.service.dto.JwtUserDto;
import com.yangxu.hospital.system.modules.system.domain.Role;
import com.yangxu.hospital.system.modules.system.domain.SysPatient;
import com.yangxu.hospital.system.modules.system.domain.User;
import com.yangxu.hospital.system.modules.system.domain.dto.UserDto;
import com.yangxu.hospital.system.modules.system.mapper.UserMapper;
import com.yangxu.hospital.system.modules.system.mapper.UserRoleMapper;
import com.yangxu.hospital.system.modules.system.service.SysPatientService;
import com.yangxu.hospital.system.modules.system.service.UserService;
import com.yangxu.hospital.tools.utils.BarcodeAndQRCodeGenerator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.result.R;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author YangXu
 * @date 2024-11-10
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Api(tags = "系统：系统授权接口")
public class AuthorizationController {
    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final OnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final UserService userService;
    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;

    private final UserRoleMapper userRoleMapper;
    private final SysPatientService sysPatientService;
    @Resource
    private LoginProperties loginProperties;

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousPostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            // 踢掉之前已经登录的token
            onlineUserService.kickOutForUsername(authUser.getUsername());
        }
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回登录信息
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<UserDetails> getUserInfo() {
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
    }

    @ApiOperation("获取验证码")
    @AnonymousGetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousDeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        String token = tokenProvider.getToken(request);
        onlineUserService.logout(token);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("用户注册")
    @ApiOperation("用户注册")
    @AnonymousPostMapping(value = "/register")
    public R register(@Validated @RequestBody AuthUserDto authUser) throws Exception {

        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        User user = new User();
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (org.springframework.util.StringUtils.hasText(authUser.getUsername())){
            // 判断 authUser.getUsername() 是不是邮箱
            if (authUser.getUsername().contains("@")) {
                // 判断 authUser.getUsername() 是不是邮箱
                user.setEmail(authUser.getUsername());
            }
            user.setUsername(authUser.getUsername());
            user.setAvatarUrl("http://file.jsmao.cn:6443/touxiang.png");
            userLambdaQueryWrapper.eq(User::getUsername, authUser.getUsername());
        }

        if (org.springframework.util.StringUtils.hasText(authUser.getPassword())){
            user.setPassword(passwordEncoder.encode(password));
        }

        // 获取当前最大的 medicalCard 值
        Long maxMedicalCard = userMapper.getMaxMedicalCard();
        Long nextMedicalCard = maxMedicalCard + 1;
        user.setMedicalCard(nextMedicalCard);
        // 分配角色
        HashSet<Role> roles = getRoles();
        List<User> list = userService.list(userLambdaQueryWrapper);
        if (list.size() > 0){
            return R.fail("用户已存在");
        }else {
            // 如果用户不存在，创建新用户
            user = createUser(user, nextMedicalCard, roles);

        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 生成条形码和二维码
        String barcodeBase64 = generateBarcodeBase64(user.getMedicalCard());
        String qrCodeBase64 = generateQRCodeBase64(user.getMedicalCard());
        UserDto userDto = new UserDto();
        BeanUtil.copyProperties(user, userDto);
        userDto.setBarcodeBase64(barcodeBase64);
        userDto.setQrCodeBase64(qrCodeBase64);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            // 踢掉之前已经登录的token
            onlineUserService.kickOutForUsername(authUser.getUsername());
        }
        return R.success(authInfo);
    }

    private HashSet<Role> getRoles() {
        HashSet<Role> roles = new HashSet<>();
        Role role = new Role();
        role.setId(3L);
        role.setDataScope("本级");
        role.setLevel(3);
        roles.add(role);
        return roles;
    }

    private User createUser(User user, Long medicalCard, HashSet<Role> roles) {
        user.setMedicalCard(medicalCard);
        user.setEnabled(true);
        userService.saveOrUpdate(user);
        // 查出刚刚添加的用户id
        User user1 = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getMedicalCard, medicalCard).eq(User::getUsername, user.getUsername()));
        // 新增用户与角色关系
        userRoleMapper.insertData(user1.getId(), roles);
        // 同步插入患者表生成患者数据
        SysPatient sysPatient = new SysPatient();
        sysPatient.setPatientId(user.getId());
        sysPatientService.create(sysPatient);
        return user;
    }

    private String generateBarcodeBase64(Long medicalCard) throws Exception {
        return BarcodeAndQRCodeGenerator.generateBarcodeBase64(String.valueOf(medicalCard), 300, 80);
    }

    private String generateQRCodeBase64(Long medicalCard) throws Exception {
        return BarcodeAndQRCodeGenerator.generateQRCodeBase64(String.valueOf(medicalCard), 400, 400);
    }
}
